package com.syl.generator.common.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.syl.generator.common.bean.DatabaseBean;
import com.syl.generator.common.bean.TableColumnBean;
import com.syl.generator.common.constant.BaseConstant;
import com.syl.generator.common.dao.MbgDbInfoMapper;
import com.syl.generator.common.dao.SqlLiteMapper;
import com.syl.generator.common.enums.DbType;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.log4j.Logger;

import java.beans.PropertyVetoException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.syl.generator.common.constant.BaseConstant.SQLLITE_DB_ID;

/**
 * @author syl
 * @create 2018-03-23 17:50
 **/
public class DbConnectionUtil {
    private static final Logger LOG = Logger.getLogger(DbConnectionUtil.class);
    public static Map<String,ComboPooledDataSource> connectionCache = new HashMap<>();
    public static Map<String,SqlSessionFactory>     sessionFactoryCache = new HashMap<>();

    public static boolean testConnection(DatabaseBean bean,String dbID){
        ComboPooledDataSource ds = getDataSourc(bean,true,dbID);
        if(ds == null)return false;
        String key = bean.getConnectionURL()+dbID;
        LOG.info(bean.getDbType()+" 连接成功");
        if(!connectionCache.containsKey(key))connectionCache.put(key,ds);
        return true;
    }

    private static ComboPooledDataSource getDataSourc(DatabaseBean bean,boolean test,String dbID){
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(bean.getDriverClass());
            ds.setJdbcUrl(bean.getConnectionURL());
            ds.setUser(bean.getUsername());
            ds.setPassword(bean.getPassword());
            ds.setBreakAfterAcquireFailure(false);
            ds.setAcquireRetryAttempts(10);
            ds.setIdleConnectionTestPeriod(30);
            ds.setMaxIdleTime(1000);//连接10秒内不使用则释放连接
            ds.setCheckoutTimeout(10000);//获取连接超时时间为10秒，默认则无限等待
            ds.setUnreturnedConnectionTimeout(2000);//连接回收超时时间 设置比maxIdleTime大
            ds.setDebugUnreturnedConnectionStackTraces(true);

            ds.getConnection();//测试连接
            if(!connectionCache.containsKey(dbID))connectionCache.put(dbID,ds);
        } catch (PropertyVetoException e) {
            LOG.error(e.getMessage());
            return null;
        } catch (SQLException e) {
            LOG.error(e.getMessage());
            LOG.info(bean.getDbType()+"连接失败了 请检查参数是否正确");
            return null;
        }
        return ds;
    }

    /**
     * 获取mybatis session
     * @param bean
     * @param dbID
     * @return
     */
    public static SqlSession getAutoSession(DatabaseBean bean,String dbID){
        SqlSessionFactory factory = getSqlSessionFactory(bean, dbID);
        //factory.cl
        if(factory == null)throw new RuntimeException("连接不上数据库");
        return factory.openSession(true);
    }

    //TODO 每次创建session工厂可能导致性能问题  待改进
    public static SqlSessionFactory getSqlSessionFactory(DatabaseBean bean,String dbID){
        String key = bean.getConnectionURL()+dbID;
        if(sessionFactoryCache.containsKey(key))return sessionFactoryCache.get(key);
        ComboPooledDataSource dataSource = connectionCache.get(key);
        if(dataSource == null){
           dataSource = getDataSourc(bean,true,dbID);
        }

        Configuration config = new Configuration();
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment(dbID, transactionFactory, dataSource);
        config.setEnvironment(environment);
        config.addMappers(ConfigReadUtil.getYmlString("db.dao"));
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(config);
        if(sessionFactory != null)sessionFactoryCache.put(key,sessionFactory);
        LOG.info("创建了  sessionFactory "+dbID);
        return sessionFactory;
    }

    /**
     * 获取数据库表或列信息
     * @param bean
     * @param type 查询类型 0:查询所有表 1:查询所有列
     * @return
     */
    public static List<TableColumnBean> getTableColumnList(DatabaseBean bean,int type){
        SqlSession session = DbConnectionUtil.getAutoSession(bean, bean.getName());
        String daoName = DbType.valueOf(bean.getDbType()).getDaoName();
        MbgDbInfoMapper mapper = session.getMapper(MbgDbInfoMapper.class);
        List<TableColumnBean> columnList = null;
        try {
            Method method = null;
            if(type == 0) {
                method = mapper.getClass().getMethod(BaseConstant.TABLE_METHOD_NAME.replace("XXX", daoName), String.class);
                columnList = (List<TableColumnBean>) method.invoke(mapper, bean.getSchema());
            }else if(type == 1) {
                method = mapper.getClass().getMethod(BaseConstant.COLUMN_METHOD_NAME.replace("XXX",daoName),String.class,String.class);
                columnList = (List<TableColumnBean>) method.invoke(mapper,bean.getSchema(),bean.getTableName());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return columnList;
    }

    public static SqlLiteMapper getSqliteDao(){
        DatabaseBean bean = new DatabaseBean(DbType.SqlLite,"",0,  "", "", "", "");
        ComboPooledDataSource dataSourc = getDataSourc(bean, false,BaseConstant.SQLLITE_DB_ID);
        try {
            LOG.info(dataSourc.getNumBusyConnections()+" 正在使用连接数");
            LOG.info(dataSourc.getNumConnections()+" 总连接数");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        SqlSession session1 = getAutoSession(bean,SQLLITE_DB_ID);
        return session1.getMapper(SqlLiteMapper.class);
    }

//    public static void closeSqliteSession(){
//        DatabaseBean bean = new DatabaseBean(DbType.SqlLite,"",0,  "", "", "", "");
//        SqlSession session1 = getAutoSession(bean,SQLLITE_DB_ID);
//        session1.close();
//    }
}
